package utils

import (
	"fmt"
	"math"
)

type Math struct {
}

var instMath *Math

func MathInst() *Math {
	if instMath == nil {
		instMath = &Math{}
	}
	return instMath
}

func (a *Math) ForecastLiner(key float64, y_Values, x_Values []float64) (float64, error) {

	var index = a.BinarySearch(x_Values, key)
	if index == -1 {
		return 0, fmt.Errorf("超过插值范围")
	}
	var Xnear = []float64{x_Values[index], x_Values[index+1]}
	var Ynear = []float64{y_Values[index], y_Values[index+1]}
	return math.Round(a.Forecast(key, Xnear, Ynear)), nil
}

// / <summary>
// / 得到平均值
// / </summary>
// / <param name="data"></param>
// / <returns></returns>
func (a *Math) GetAverage(data []float64) float64 {
	var total float64 = 0
	for i := 0; i < len(data); i++ {
		total += data[i]
	}

	return total / float64(len(data))
}

func (a *Math) DEVSQ(arr ...float64) float64 {
	var mu = a.GetAverage(arr)
	var tot float64 = 0.0
	for _, f := range arr {
		tot += math.Pow(f-mu, 2)
	}
	return tot
}

func (a *Math) Forecast(key float64, y_map, x_map []float64) float64 {
	Debug("Forecast key %v , y_map %+v , x_map %+v", key, y_map, x_map)
	var x_Avg = a.GetAverage(x_map)
	var y_Avg = a.GetAverage(y_map)

	var forecast float64 = 0
	var b float64 = 0
	var a1 float64 = 0

	var tempTop float64 = 0
	var tempBottom float64 = 0

	for i := 0; i < len(y_map); i++ {
		tempTop += (x_map[i] - x_Avg) * (y_map[i] - y_Avg)
		tempBottom += math.Pow((x_map[i] - x_Avg), 2)
	}

	b = tempTop / tempBottom
	a1 = y_Avg - b*x_Avg

	forecast = a1 + b*key

	return forecast
}

// / <summary>
// / 二分查找
// / </summary>
// / <param name="dlist"></param>
// / <param name="x"></param>
// / <returns></returns>
func (a *Math) BinarySearch(dlist []float64, x float64) int {
	if x < dlist[0] || x > dlist[len(dlist)-1] {
		return -1
	}

	var low = 0
	var high = len(dlist) - 1

	for {
		if low > high {
			break
		}
		var middle = (low + high) / 2
		if middle == len(dlist)-1 {
			return middle
		}
		if dlist[middle] == x {
			return middle
		} else {
			if dlist[middle] < x {

				if x < dlist[middle+1] {
					return middle
				} else {
					low = middle + 1
					continue
				}
			} else {

				if x > dlist[middle-1] {
					return middle - 1
				} else {
					high = middle - 1
					continue
				}
			}
		}
	}
	return -1
}

/*
public class ExcelInterAlgorithm
    {
       public static double Compute(double[] x_Values, double[] y_Values, double key)
       {
           int index = binarySearch(x_Values,key);
           if (index == -1)
           {
               throw new IndexOutOfRangeException("超过插值范围");
           }
           double[] Xnear = new double[2] { x_Values[index], x_Values[index + 1] };
           double[] Ynear = new double[2] { y_Values[index], y_Values[index + 1] };
           return Math.Round(getValue(Xnear, Ynear, key), 3);
       }

        private static double getValue(double[] x_map, double[] y_map, double key)
       {
           double x_Avg = GetAverage(x_map);
           double y_Avg = GetAverage(y_map);


           double forecast = 0f;
           double b = 0f;
           double a = 0f;


           double tempTop = 0f;
           double tempBottom = 0f;


           for (int i = 0; i < y_map.Length; i++)
           {
               tempTop += (x_map[i] - x_Avg) * (y_map[i] - y_Avg);
               tempBottom += Math.Pow(((x_map[i] - x_Avg)), 2f);
           }


           b = tempTop / tempBottom;
           a = y_Avg - b * x_Avg;


           forecast = a + b * key;


           return forecast;
       }

       /// <summary>
       /// 得到平均值
       /// </summary>
       /// <param name="data"></param>
       /// <returns></returns>
       private static double GetAverage(double[] data)
       {
           double total = 0;
           for (int i = 0; i < data.Length; i++)
           {
               total += data[i];
           }

           return total / data.Length;
       }
       /// <summary>
       /// 二分查找
       /// </summary>
       /// <param name="dlist"></param>
       /// <param name="x"></param>
       /// <returns></returns>
       public static int binarySearch(double[] dlist, double x)
       {
           if (x < dlist[0] || x > dlist[dlist.Length - 1]) return -1;

           int low = 0;
           int high = dlist.Length - 1;

           while (low <= high)
           {
               int middle = (low + high) / 2;
               if (middle == dlist.Length - 1)
               {
                   return middle;
               }
               if (dlist[middle] == x)
               {
                   return middle;
               }
               else
               {
                   if (dlist[middle] < x)
                   {

                       if (x < dlist[middle + 1])
                       {
                           return middle;
                       }
                       else
                       {
                           low = middle + 1;
                           continue;
                       }
                   }
                   else
                   {

                       if (x > dlist[middle - 1])
                       {
                           return middle - 1;
                       }
                       else
                       {
                           high = middle - 1;
                           continue;
                       }
                   }
               }
           }
           return -1;
       }


*/
